#!/usr/bin/python3
# -*- coding: utf-8 -*-

import os
import sys
import time
import fcntl
import threading
import subprocess

sys.path.insert(0, os.path.expanduser('~') + '/lib/python')

import mylib.util as util
import mylib.config as config
import mylib.timer as timer

'''
Dialog	tkinter.dialog
FileDialog	tkinter.FileDialog
ScrolledText	tkinter.scolledtext
SimpleDialog	tkinter.simpledialog
Tix	tkinter.tix
Tkconstants	tkinter.constants
Tkdnd	tkinter.dnd
tkColorChooser	tkinter.colorchooser
tkCommonDialog	tkinter.commondialog
tkFileDialog	tkinter.filedialog
tkFont	tkinter.font
tkMessageBox	tkinter.messagebox
tkSimpleDialog	tkinter.simpledialog

'''

try: import ttk
except ImportError: import tkinter.ttk as ttk
try: import Tkinter as tk  # Python 2
except ImportError: import tkinter as tk  # Python 3
try: import tkFont
except ImportError: import tkinter.font as tkFont


class MPlayer(threading.Thread):
    done = False

    PAUSE = 'pause'
    PLAY = 'play'
    STOP = 'stop'
    LOAD = 'load'

    def __init__(self):
        threading.Thread.__init__(self)
        self.player = subprocess.Popen(
            ['mplayer', '-slave', '-idle', '-really-quiet', '-ao', 'pulse', '-vo', 'null', '-msglevel', 'global=5'],
            shell=False, stdout=subprocess.PIPE, stdin=subprocess.PIPE)
        self.song = ''
        self.cur_seconds = 0.0
        self.total_seconds = 0.0
        self.volume = 50.0
        self.status = self.STOP
        self.codec = ''
        self.bitrate = ''
        self.samples = ''

    def pipe_read(self, pipe):
        fd = pipe.fileno()
        fl = fcntl.fcntl(fd, fcntl.F_GETFL)
        fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)
        try:
            pipe.flush()
            ba = pipe.readline()
            s = ba.decode()
            return s
        except:
            return ''

    def player_cmd(self, cmd):
        cmd += '\n'
        try:
            self.player.stdin.write(cmd.encode())
        except:
            self.player.stdin.write(cmd)
        self.player.stdin.flush()

    def is_playing(self):
        return self.status == self.PLAY

    def load(self, file):
        self.player_cmd('loadfile "' + file + '"')
        self.status = self.PLAY
        self.player_cmd('pausing_keep_force get_audio_codec')
        self.player_cmd('pausing_keep_force get_audio_bitrate')
        self.player_cmd('pausing_keep_force get_audio_samples')
        self.player_cmd('pausing_keep_force get_property length')
        self.set_volume(self.volume)
        self.song = os.path.basename(file)
        print(self.song)

    def play(self):
        if self.status == self.PAUSE:
            self.player_cmd('pause')
        else:
            self.player_cmd('play')
        self.status = self.PLAY

    def pause(self):
        if self.status != self.PAUSE:
            self.player_cmd('pause')
            self.status = self.PAUSE

    def stop(self):
        self.player_cmd('stop')
        self.status = self.STOP

    def quit(self):
        self.player_cmd('stop')
        self.player_cmd('quit')
        self.done = True

    def set_volume(self, vol):
        self.volume = vol
        self.player_cmd('pausing_keep_force set_property volume ' + str(vol))

    def get_status(self):
        return self.status

    def get_total_seconds(self):
        return self.total_seconds

    def get_current_seconds(self):
        return self.cur_seconds

    def set_current_seconds(self, seconds):
        self.player_cmd('pausing_keep_force seek ' + str(seconds) + ' 2')
        return self.cur_seconds

    def parse(self, cmd, value):
        if cmd == 'ANS_time_pos':
            cur_seconds = float(value)
            self.cur_seconds = cur_seconds
        elif cmd == 'ANS_AUDIO_CODEC':
            self.codec = value.replace("'", "")
        elif cmd == 'ANS_AUDIO_BITRATE':
            self.bitrate = value.replace("'", "")
        elif cmd == 'ANS_AUDIO_SAMPLES':
            self.samples = value.replace("'", "")
        elif cmd == 'ANS_length':
            tot_seconds = float(value)
            self.total_seconds = tot_seconds
        elif value == 'PROPERTY_UNAVAILABLE':
            self.status = self.STOP
        else:
            print((cmd, value))

    def run(self):
        # Run your code here
        print('Start Player Thread')
        while not self.done:
            while True:
                s = self.pipe_read(self.player.stdout)
                if len(s) > 0:
                    cmd, value = s.split('=', 1)
                else:
                    break
                self.parse(cmd, value.strip())
            if self.status == self.PLAY:
                self.player_cmd('pausing_keep_force get_property time_pos')
            time.sleep(0.25)
        print('Player Stop!!')


class PlayerApp(tk.Tk):
    STOP = 0
    PLAY = 1
    PAUSE = 2
    PAUSE_PLAY = 3

    CB_WIDTH = 18
    CB_HEIGHT = 18
    BORDER = 3
    TITLE_HEIGHT = 23

    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)
        #self.overrideredirect(True)
        self.bind("<Configure>", self.on_resize)

        self.geo = self.wm_geometry()
        self.x, self.y = self.winfo_x(), self.winfo_y()
        self.width, self.height = self.winfo_width(), self.winfo_height()
        self.list_height = 0
        self.mx = None
        self.my = None

        # fonts for all widgets
        self.option_add("*Font", '"굴림" 9')

        # font to use for label widgets
        self.option_add("*Label.Font", '"굴림" 11')

        self.option_add("*Button.Font", '"굴림" 8')

        self.caption_font = tkFont.Font()
        # use gold/black for selections
        self.option_add("*selectBackground", "gold")
        self.option_add("*selectForeground", "black")
        '''
        Caption Frame ----------------------------------------------------------------------------

        self.caption_frame = tk.Frame(self, height=18)  # , height=20)
        self.caption_frame.pack(side="top", fill="both", expand=False)

        self.caption_text = tk.StringVar()
        self.caption = tk.Label(self.caption_frame, text="Title", bg="#ccc", textvariable=self.caption_text)
        # self.caption.pack(in_=self.caption_frame, side="left", fill="both", expand=True)
        self.caption.place(x=0, y=0, width=self.width - 21, height=18)
        self.caption_text.set('My Media Player')
        self.caption.bind("<ButtonPress-1>", self.on_start_move)
        self.caption.bind("<ButtonRelease-1>", self.on_stop_move)
        self.caption.bind("<B1-Motion>", self.on_motion)

        self.close_btn = tk.Button(self.caption_frame, text='X', command=self.exit)
        self.mini_btn = tk.Button(self.caption_frame, text='_', command=self.minimize)

        self.caption.place(x=0, y=0, width=self.width - self.CB_WIDTH * 2 - 1, height=18)
        self.close_btn.place(x=self.width - self.CB_WIDTH, y=0, width=self.CB_WIDTH, height=self.CB_HEIGHT)
        self.mini_btn.place(x=self.width - self.CB_WIDTH * 2, y=0, width=self.CB_WIDTH, height=self.CB_HEIGHT)
        '''
        '''
        Play Info --------------------------------------------------------------------------------
        '''
        self.play_frame = tk.Frame(self, bg='#efefef')
        self.play_frame.pack(side="top", fill="both", expand=False)

        self.title_text = tk.StringVar()
        self.label = tk.Label(self, text="Title", textvariable=self.title_text)
        self.label.pack(in_=self.play_frame, side="top", fill="both", expand=True)
        self.title_text.set('Title')

        self.info_text = tk.StringVar()
        self.info = tk.Label(self, text="Info", textvariable=self.info_text)
        self.info.pack(in_=self.play_frame, side="top", fill="both", expand=True)
        self.info_text.set('Info')

        self.play_pos_slider = tk.Scale(self, from_=0, to=100, resolution=0.1, sliderlength=20, orient=tk.HORIZONTAL,
                                        showvalue=0, width=10, length=350)
        self.play_pos_slider.pack(in_=self.play_frame, side="left")
        self.play_pos_slider.bind("<ButtonRelease-1>", self.set_play_pos)
        self.play_pos_slider.bind("<Button-1>", self.set_play_pos_start)

        self.volume_slider = tk.Scale(self, from_=0, to=100, resolution=0.1, sliderlength=20, orient=tk.HORIZONTAL,
                                      showvalue=0, width=10, length=100)
        self.volume_slider.pack(in_=self.play_frame, side="left")
        self.volume_slider.bind("<ButtonRelease-1>", self.set_volume_pos)

        self.play_pos_move_start = False

        '''
        Control Button ---------------------------------------------------------------------------
        '''

        self.line_frame = tk.Frame(self, height=20, bg='#eee')
        self.line_frame.pack(side="top", fill="both", expand=False)

        self.left_buttons = []
        self.right_buttons = []
        self.btn_txts = []
        ltxts = ['Play', 'Stop', 'Prev', 'Next']
        lcmds = [self.player_play_pause, self.player_stop, self.player_play_prev, self.player_play_next]
        rtxts = ['H', 'P', 'F']
        rcmds = [self.show_hide_list, self.show_play_list, self.show_file_list]
        for i in range(0, len(ltxts)):
            txt = tk.StringVar()
            self.btn_txts.append(txt)
            self.btn_txts[i].set(ltxts[i])
            # img_path = os.path.expanduser('~') + '/.local/share/mmp/skin/default/play.png'
            # img = tk.PhotoImage(file=img_path)
            btn = tk.Button(self.line_frame, textvariable=self.btn_txts[i], command=lcmds[i])
            btn.place(x=i * 46 + 1, y=1, width=45, height=18)
            self.left_buttons.append(btn)

        for i in range(0, len(rtxts)):
            # img_path = os.path.expanduser('~') + '/.local/share/mmp/skin/default/play.png'
            # img = tk.PhotoImage(file=img_path)
            btn = tk.Button(self.line_frame, text=rtxts[i], command=rcmds[i])
            p = self.width - ((i + 1) * 20 + 5)
            btn.place(x=p, y=1, width=20, height=18)
            self.right_buttons.append(btn)

        '''
        Play List & File List --------------------------------------------------------------------
        '''
        self.list_frame = tk.Frame(self)
        self.list_frame.pack(side="bottom", fill="both", expand=True)

        self.play_list = tk.Listbox(self.list_frame, selectbackground='#fff', selectmode=tk.EXTENDED)
        self.play_list.bind("<Double-Button-1>", self.on_double_click_play_list)
        self.play_list.bind("<Button-3>", self.on_play_list_pop)

        self.file_list = tk.Listbox(self.list_frame, selectbackground='#fff', selectmode=tk.EXTENDED)
        self.file_list.bind("<Double-Button-1>", self.on_double_click_file_list)
        self.file_list.bind("<Button-3>", self.on_file_list_pop)

        self.play_list_index = 0
        self.file_list_index = 0
        self.active_list = 1

        self.show_file_list()
        '''
        # self.grip = tk.Label(self, text="|")
        self.grip = ttk.Sizegrip(self)
        self.grip.place(relx=1.0, rely=1.0, anchor="se")
        self.grip.lift(self.list_frame)
        self.grip.bind("<B1-Motion>", self.OnMotion)
        '''
        # create a menu
        self.file_list_popup = tk.Menu(self, tearoff=0)
        self.file_list_popup.add_command(label="Add to Playlist", command=self.on_file_list_popup_add)
        self.file_list_popup.add_command(label="Replace to Playlist", command=self.on_file_list_popup_replace)
        self.file_list_popup.add_separator()
        self.file_list_popup.add_command(label="None")

        self.play_list_popup = tk.Menu(self, tearoff=0)
        self.play_list_popup.add_command(label="Play", command=self.on_play_list_popup_play)
        self.play_list_popup.add_command(label="Remove", command=self.on_play_list_popup_remove)
        self.play_list_popup.add_command(label="Remove All", command=self.on_play_list_popup_remove_all)
        self.play_list_popup.add_separator()
        self.play_list_popup.add_command(label="None")

        self.data = []
        self.list = []

        self.load_config()

        self.play_status = self.STOP
        self.player = MPlayer()
        self.player.start()

        self.volume_slider.set(self.player.volume)

        self.timer = timer.MyTimer(0.5, self.update_info)

    def save_config(self):
        c = config.Config(os.path.expanduser('~') + '/.local/share/mmp/config')
        c.set('Config', 'geomerty', self.geo)
        c.set('Config', 'list_height', str(self.list_height))
        c.save()

    def load_config(self):
        c = config.Config(os.path.expanduser('~') + '/.local/share/mmp/config')
        c.load()

        height = c.get('Config', 'list_height')
        if height is not None and int(height) > 0:
            self.show_hide_list()
            self.list_height = int(height)

        geo = c.get('Config', 'geomerty')
        self.geometry(geo)

    def on_start_move(self, event):
        self.mx = event.x
        self.my = event.y

    def on_stop_move(self, event):
        self.mx = None
        self.my = None

    def on_motion(self, event):
        delta_x = event.x - self.mx
        delta_y = event.y - self.my
        x = self.winfo_x() + delta_x
        y = self.winfo_y() + delta_y
        self.geometry("+%s+%s" % (x, y))

    def OnMotion(self, event):
        x1 = self.winfo_pointerx()
        y1 = self.winfo_pointery()
        x0 = self.winfo_rootx()
        y0 = self.winfo_rooty()
        self.geometry("%sx%s" % ((x1 - x0), (y1 - y0)))
        return
    '''
    def raise_above_all(self, flag=True):
        self.attributes('-topmost', flag)

    def minimize(self):
        self.withdraw()
        # self.attributes('-topmost', 0)
        # self.lower(self.winfo_toplevel())
        # self.org_geo = self.winfo_geometry()
        # self.geometry("2000x0")
        # self.lower(self.winfo_toplevel())

        # self.overrideredirect(False)
        # self.iconify()
        # self.overrideredirect(True)

        # self.wm_state('iconic')
        # self.iconify()
    '''
    def on_resize(self, event):
        if self.geo == self.wm_geometry():
            pass
        else:
            self.geo = self.wm_geometry()
            self.x, self.y = self.winfo_x(), self.winfo_y()
            self.width, self.height = self.winfo_width(), self.winfo_height()
            # print(self.geo)
            win_geo = self.geo
            inner_geo = self.winfo_geometry()
            xy1 = inner_geo.replace('-', '+').split('+')
            xy2 = win_geo.replace('-', '+').split('+')
            self.BORDER = abs(int(xy1[1]) - int(xy2[1]))
            self.TITLE_HEIGHT = abs(int(xy1[2]) - int(xy2[2]))
            # print(xy1, xy2)

            '''
            # self.close_btn.place(x=self.width-20, y=1, width=20, height=18)
            self.caption.place(x=0, y=0, width=self.width - self.CB_WIDTH * 2 - 1, height=18)
            self.close_btn.place(x=self.width - self.CB_WIDTH, y=0, width=self.CB_WIDTH, height=self.CB_HEIGHT)
            self.mini_btn.place(x=self.width - self.CB_WIDTH * 2, y=0, width=self.CB_WIDTH, height=self.CB_HEIGHT)
            '''
            w = self.width - 108
            self.play_pos_slider.config(length=w)
            # self.play_pos_slider.place(width=w)
            for i in range(0, len(self.right_buttons)):
                btn = self.right_buttons[i]
                p = self.width - ((i + 1) * 21 + 1)
                btn.place(x=p, y=1, width=20, height=18)

    def set_play_pos_start(self, event):
        self.play_pos_move_start = True

    def set_volume_pos(self, event):
        pos = self.volume_slider.get()
        self.player.set_volume(pos)

    def set_play_pos(self, event):
        pos = self.play_pos_slider.get()
        print(str(pos) + ' %')
        self.player.set_current_seconds(pos * self.player.total_seconds / 100.0)
        self.play_pos_move_start = False

    def update_info(self):
        if self.play_pos_move_start:
            pass
        elif self.player.is_playing():
            if self.player.total_seconds > 0.1:
                self.title_text.set(self.player.song)
                self.info_text.set(
                    "[" + time.strftime("%H:%M:%S", time.gmtime(self.player.cur_seconds)) + "/" + time.strftime(
                        "%H:%M:%S", time.gmtime(
                            self.player.total_seconds)) + "] - " + self.player.codec + ', ' + self.player.bitrate + ', ' + self.player.samples)
                pos = 100.0 * self.player.cur_seconds / self.player.total_seconds
                self.play_pos_slider.set(pos)
            self.play_status = self.PLAY
        elif self.play_status == self.PAUSE_PLAY:
            print('Pause Play')
            self.player.play()
            self.play_status = self.PAUSE
        elif self.play_status == self.PLAY:
            self.player_play_next()
            # elif self.play_status != self.STOP:
            # self.player_stop()

    def player_play_next(self):
        print("Play Next")
        if abs(self.active_list) == 1:
            self.file_list_index += 1
            if self.file_list_index < len(self.data):
                # self.file_list.selection_clear(0, tk.END)
                # self.file_list.select_set(self.file_list_index)
                self.file_list.see(self.file_list_index)
                self.player.load(self.data[self.file_list_index][1])
                self.play_status = self.PLAY
            else:
                self.player_stop()
        elif abs(self.active_list) == 2:
            self.play_list_index += 1
            if self.play_list_index < len(self.list):
                # self.play_list.selection_clear(0, tk.END)
                # self.play_list.select_set(self.play_list_index)
                self.play_list.see(self.play_list_index)
                self.player.load(self.list[self.play_list_index][1])
                self.play_status = self.PLAY
            else:
                self.player_stop()

    def player_play_prev(self):
        print("Play Prev")
        if abs(self.active_list) == 1:
            self.file_list_index -= 1
            if self.file_list_index < 0:
                self.file_list_index = 0
            # self.file_list.selection_clear(0, tk.END)
            # self.file_list.select_set(self.file_list_index)
            self.file_list.see(self.file_list_index)
            self.player.load(self.data[self.file_list_index][1])
            self.play_status = self.PLAY
        elif abs(self.active_list) == 2:
            self.play_list_index -= 1
            if self.play_list_index < 0:
                self.play_list_index = 0
            # self.play_list.selection_clear(0, tk.END)
            # self.play_list.select_set(self.play_list_index)
            self.play_list.see(self.play_list_index)
            self.player.load(self.list[self.play_list_index][1])
            self.play_status = self.PLAY

    def player_play_pause(self):
        if self.player.is_playing():
            self.player.pause()
            self.play_status = self.PAUSE
            self.btn_txts[0].set('Play')
            print("Play Pause")
        else:
            self.play_status = self.PAUSE_PLAY
            self.btn_txts[0].set('Pause')
            print("Play Play")

    def player_stop(self):
        self.player.stop()
        self.title_text.set('Title')
        self.info_text.set('Info')
        self.btn_txts[0].set('Play')
        self.play_status = self.STOP
        self.file_list_index = 0
        self.play_list_index = 0
        print("Play Stop")

    def exit(self, event=None):
        self.save_config()
        self.timer.stop()
        self.player.quit()
        self.data = []
        self.list = []
        exit()

    def set_win_size(self):
        geostr = str(self.width) + 'x' + str(self.height) + \
                 '+' + str(self.x - self.BORDER) + '+' + str(self.y - self.TITLE_HEIGHT)
        # geostr = str(self.width) + 'x' + str(self.height) + '+' + str(self.x) + '+' + str(self.y)
        # self.geometry(geostr)
        self.wm_geometry(geostr)

    def show_hide_list(self, event=None):
        if self.active_list > 0:
            self.active_list = -self.active_list
            self.list_height = self.list_frame.winfo_height()
            self.height -= self.list_height

            self.set_win_size()
            self.file_list.pack_forget()
            self.play_list.pack_forget()
        elif abs(self.active_list) == 1:
            self.show_file_list()
        elif abs(self.active_list) == 2:
            self.show_play_list()

    def show_play_list(self, event=None):
        self.active_list = 2
        if self.list_height > 0:
            self.height += self.list_height
            self.list_height = 0
            self.set_win_size()
        self.file_list.pack_forget()
        self.play_list.pack(in_=self.list_frame, side="top", fill="both", expand=True)

    def show_file_list(self, event=None):
        self.active_list = 1
        if self.list_height > 0:
            self.height += self.list_height
            self.list_height = 0
            self.set_win_size()
        self.play_list.pack_forget()
        self.file_list.pack(in_=self.list_frame, side="top", fill="both", expand=True)

    def cd(self, path):
        print("cd( " + path + " )")
        dl, fl = util.get_folder_and_file_names(path)

        self.file_list.delete(0, tk.END)  # clear
        self.data = []

        self.file_list.insert(tk.END, "..")
        self.data.append(("..", os.path.abspath(path + os.sep + "..")))
        for itm in dl:
            self.file_list.insert(tk.END, itm)
            self.data.append((itm, path + os.sep + itm))

        for itm in fl:
            self.file_list.insert(tk.END, itm)
            self.data.append((itm, path + os.sep + itm))

    # File List Popup Menu Functions -------------------------------------------------------------
    def on_file_list_pop(self, event):
        selection = self.file_list.curselection()
        if len(selection) > 0:
            # display the popup menu
            try:
                self.file_list_popup.tk_popup(event.x_root, event.y_root, 0)
            finally:
                # make sure to release the grab (Tk 8.0a1 only)
                self.file_list_popup.grab_release()

    def on_file_list_popup_add(self):
        selection = self.file_list.curselection()
        for sel in selection:
            self.play_list.insert(tk.END, self.data[sel][0])
            self.list.append(self.data[sel])
            print(sel, self.data[sel])

    def on_file_list_popup_replace(self):
        selection = self.file_list.curselection()
        self.play_list.delete(0, tk.END)  # clear
        self.list = []

        for sel in selection:
            self.play_list.insert(tk.END, self.data[sel][0])
            self.list.append(self.data[sel])
            print(sel, self.data[sel])

    # Play List Popup Menu Functions -------------------------------------------------------------
    def on_play_list_pop(self, event):
        selection = self.play_list.curselection()
        if len(selection) > 0:
            # display the popup menu
            try:
                self.play_list_popup.tk_popup(event.x_root, event.y_root, 0)
            finally:
                # make sure to release the grab (Tk 8.0a1 only)
                self.play_list_popup.grab_release()

    def on_play_list_popup_play(self):
        pass

    def on_play_list_popup_remove(self):
        selection = self.play_list.curselection()
        selection = reversed(selection)
        for sel in selection:
            print(sel, self.list[sel])
            self.play_list.delete(sel)
            self.list.remove(self.list[sel])

    def on_play_list_popup_remove_all(self):
        self.play_list.delete(0, tk.END)  # clear
        self.list = []

    # List Double Click Functions ----------------------------------------------------------------
    def on_double_click_file_list(self, event):
        widget = event.widget
        selection = widget.curselection()
        if len(selection) > 0:
            idx = selection[0]
            full = self.data[idx][1]
            if os.path.isdir(full):
                self.cd(full)
            else:
                self.play_status = self.PLAY
                self.file_list_index = idx
                self.btn_txts[0].set('Pause')
                self.player.load(full)

    def on_double_click_play_list(self, event):
        widget = event.widget
        selection = widget.curselection()
        if len(selection) > 0:
            idx = selection[0]
            full = self.list[idx][1]
            if os.path.isdir(full):
                self.cd(full)
            else:
                self.play_status = self.PLAY
                self.play_list_index = idx
                self.btn_txts[0].set('Pause')
                self.player.load(full)


if __name__ == "__main__":
    app = PlayerApp()

    icon = 'skin/App media player.xbm'
    conf_path = os.path.expanduser('~') + '/.local/share/mmp/'

    app.title("My Media Player")
    app.wm_iconbitmap(bitmap="@" + conf_path + icon)
    app.minsize(width=350, height=74)
    app.maxsize(width=600, height=800)
    app.cd("/media/06Tera1/Music")
    app.mainloop()
    app.exit()
